package internal

import (
	"bytes"
	"db_doc_generator/config"
	"db_doc_generator/db"
	"fmt"
	"io/ioutil"
	"log"
	"strings"
)

type ColumnInfo struct {
	// 列名
	ColumnName string
	// 列描述
	ColumnDesc string
	// 列类型
	ColumnType string
	// 是否可以为空
	Nullable bool

	ColumnRemark string
}

var ContentBuffer bytes.Buffer = bytes.Buffer{}

func Gen() {
	modules := config.CONF_INSTANCE.Modules
	var tableCount int
	if len(modules) == 0 {
		log.Fatal("未配置module")
	}
	for _, v := range modules {
		name := v.Name
		tables := v.Tables
		WriteModuleName(name)
		for _, t := range tables {
			WriteTableInfo(t)
			tableCount++
		}
	}

	ioutil.WriteFile("table.md", ContentBuffer.Bytes(), 0777)
	fmt.Printf("生成了%d个表的文档", tableCount)
}

// 输出module名称
func WriteModuleName(moduleName string) {
	ContentBuffer.WriteString(fmt.Sprintf("## %s\n", moduleName))
}

// 输出表格信息
func WriteTableInfo(tableName string) {
	if !isTableExist(tableName) {
		fmt.Println("表:" + tableName + "不存在")
		return
	}
	tableDesc := getTableDesc(tableName)
	tableTitleRemark := getTableTitleRemark(tableName)
	tableRemark := getTableRemark(tableName)
	ContentBuffer.WriteString(fmt.Sprintf("### <span id='%s'>%s (%s) %s</span> \n", "new_"+tableName, tableName, tableDesc, tableTitleRemark))
	if tableRemark != "" {
		ContentBuffer.WriteString(tableRemark + "\n\n")
	}
	// 输出表格信息
	// 输出表头
	ContentBuffer.WriteString("| Name | Type | Description |Required| Remark | \n")
	ContentBuffer.WriteString("| ---- | ---- | ---- |---- | ---- | \n")
	//输出表格内容
	tableColumns := getTableColumns(tableName)
	for i, tableColumn := range tableColumns {
		var remark string
		if tableColumn.ColumnName == "id" {
			remark = "主键"
		}
		if i == 1 {
			tableNameAndId := tableName + "_id"
			if strings.Contains(tableNameAndId, tableColumn.ColumnName) {
				remark = "联合主键"
			}
		}
		if remark == "" {
			remark = tableColumn.ColumnRemark
		}
		var required string = ""
		if tableColumn.Nullable {
			required = "optional"
		} else {
			required = "required"
		}
		tableColumn.ColumnName = strings.ReplaceAll(tableColumn.ColumnName, "\n", "")
		tableColumn.ColumnName = strings.ReplaceAll(tableColumn.ColumnName, "\r", "")

		ContentBuffer.WriteString(fmt.Sprintf("| %s | %s | %s | %s | %s | \n", tableColumn.ColumnName, tableColumn.ColumnType, tableColumn.ColumnDesc, required, remark))
	}
}

// 获取表备注
func getTableRemark(table string) string {
	baseSql := `
SELECT remark
FROM
	gen_table_config
WHERE
	table_name = ?`
	var remark string
	db.DB.Raw(baseSql, table).Scan(&remark)
	return remark
}

// 获取表头备注
func getTableTitleRemark(table string) string {
	baseSql := `
SELECT title_remark
FROM
	gen_table_config
WHERE
	table_name = ?`
	var remark string
	db.DB.Raw(baseSql, table).Scan(&remark)
	return remark
}

// 判断表是否存在
func isTableExist(table string) bool {
	baseSql := "SELECT count(*) FROM information_schema.tables WHERE table_schema =  CURRENT_SCHEMA() AND table_name = ? AND table_type = 'BASE TABLE'"
	var count int
	db.DB.Raw(baseSql, table).Scan(&count)
	if count > 0 {
		return true
	}
	return false
}

//获取表描述信息
func getTableDesc(table string) string {
	baseSql := `
SELECT CAST
	( obj_description ( relfilenode, 'pg_class' ) AS VARCHAR ) AS COMMENT 
FROM
	pg_class C 
WHERE
	relname = ?`
	var tableDesc string
	db.DB.Raw(baseSql, table).Scan(&tableDesc)
	if tableDesc == "" {
		baseSql = `select title from gen_table_config where table_name = ?`
		db.DB.Raw(baseSql, table).Scan(&tableDesc)
	}
	return tableDesc
}

func getTableColumns(table string) []ColumnInfo {
	baseSql := `
SELECT A.attname AS column_name,
	concat_ws ( '', T.typname, SUBSTRING ( format_type ( A.atttypid, A.atttypmod ) FROM '\(.*\)' ) ) AS column_type,
	d.description AS column_desc,
	( CASE A.attnotnull WHEN 't' THEN FALSE ELSE TRUE END ) AS nullable
FROM
	pg_class
	C INNER JOIN pg_attribute A ON A.attrelid = C.oid
	AND A.attnum > 0
	and  A.attname not like '%pg.dropped%'
	left JOIN pg_type T ON A.atttypid = T.oid
	left JOIN pg_description d ON d.objoid = A.attrelid
	AND d.objsubid = A.attnum

WHERE
	C.relname = ?
`
	var columnInfos []ColumnInfo
	db.DB.Raw(baseSql, table).Scan(&columnInfos)
	beforeColumns := make([]ColumnInfo, 0)
	afterColumns := make([]ColumnInfo, 0)
	for _, info := range columnInfos {
		if info.ColumnName == "created_at" ||
			info.ColumnName == "created_by" ||
			info.ColumnName == "updated_at" ||
			info.ColumnName == "updated_by" ||
			info.ColumnName == "deleted_at" ||
			info.ColumnName == "deleted_by" ||
			info.ColumnName == "is_deleted" {
			afterColumns = append(afterColumns, info)
		} else {
			beforeColumns = append(beforeColumns, info)
		}
	}
	return append(beforeColumns, afterColumns...)
}
